0x00 事情的由来
昨天看到Dubbo公布了几个新的CVE,一个没有Credit的中危 CVE-2021-25641,一个有Credit(Github Security Lab)的低危 CVE-2021-30181,其中CVE-2021-30181漏洞邮件标题为“RCE on customers via Script route poisoning (Nashorn script injection)”,看起来是route的地方出现的问题,执行了外部传入的script,导致RCE。
(邮件内容图)
按照邮件内容描述,可以知道通过配置中心(zk等),可以动态配置js脚本的路由策略,在consumer调用provider的时候,触发其执行,如果恶意用户在未授权或弱口令、没有ACL控制的zookeeper中插入恶意js脚本,将会导致consumer批量执行恶意代码。而且,这会立马执行,因为consumer会定时RPC发送心跳包到provider。
0x01 代码触发点
根据我对Dubbo代码的微末了解,以及git commit的一些信息,可以判断出,触发点相关联的部分代码应该位于ClusterInterceptor,这个拦截器主要处理一些分布式集群相关的逻辑,跟入代码,可以发现,路由选择的触发栈是这样的:
1 |
|
但我在debug的时候只看到了MockInvokersSelector、TagRouter、AppRouter、ServiceRouter这四个会默认存在,其中AppRouter、ServiceRouter这两个router都是condition相关的,而MockInvokersSelector用于支持mock特性,TagRouterRule是通过yaml配置进行对特定ip打tag实现特定的路由选择。其中并没有看到ScriptRouter这个关键route。
再次查找ScriptRouter构造的相关代码,找到了它是通过ScriptRouterFactory进行构造的,它有四个URL的构造例子,分别为:
1 |
|
其中,rule参数,被作为了js脚本传入,当执行ScriptRouter进行路由选择时,会触发其脚本代码的执行。
0x02 注入恶意脚本
前面只说到了触发恶意脚本执行的调用栈,以及所需要的Router,但是还没有说这个ScriptRoute需要如何触发构造。
一般情况下,URL的传入只能通过Dubbo的配置注解或XML配置文件进行配置,但是如果只是这样的话,未免利用条件就太过于苛刻了,按照Dubbo开发的尿性,根本不可能认可这是漏洞,最起码得能通过Zookeeper这样的配置中心传入利用。
先说一下consumer,当consumer启动时,会根据interface配置(例:com.threedr3am.learn.server.boot.DemoService),启动Zookeeper监听目录节点目录 /dubbo/com.threedr3am.learn.server.boot.DemoService下的三个node目录
configurators
providers
routers
当这些node下的内容变动时,consumer的Zookeeper监听器会watch到,然后通知到org.apache.dubbo.registry.integration.RegistryDirectory#notify方法,更新相关数据。
Zookeeper监听器订阅相关代码位于org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe方法。
理论上我们只需要在routers这个node下新增配置,插入恶意代码,就能触发其执行恶意代码,先从consumers下获取一个模板配置,例/dubbo/com.threedr3am.learn.server.boot.DemoService/consumers下的配置:
1 |
|
通过对其解码,得到很清晰的内容
1 |
|
把它修改为
1 |
|
注意事项:
rule参数下的js代码需要先编码一下再放进去,未编码数据为:
1
s=[3];s[0]='/bin/bash';s[1]='-c';s[2]='open -a calculator';java.lang.Runtime.getRuntime().exec(s);
category参数需要改为routers
- protocol需要改为script
最后编码得到
1 |
|
接下来,把这个配置新增到Zookeeper的/dubbo/com.threedr3am.learn.server.boot.DemoService/routers,就能触发代码执行了。